package com.tsfyun.scm.service.impl.finance;

import cn.hutool.core.lang.Snowflake;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.tsfyun.common.base.dto.TaskDTO;
import com.tsfyun.common.base.enums.SerialNumberTypeEnum;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.domain.DomainTypeEnum;
import com.tsfyun.common.base.enums.domain.ExpPaymentAccountStatusEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.LocalDateTimeUtils;
import com.tsfyun.common.base.util.NumberConvertUtil;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.scm.dto.finance.ExpConfirmBankDTO;
import com.tsfyun.scm.dto.finance.ExpPaymentAccountDTO;
import com.tsfyun.scm.dto.finance.ExpPaymentAccountQTO;
import com.tsfyun.scm.dto.finance.ExpPaymentCompletedDTO;
import com.tsfyun.scm.dto.support.TaskNoticeContentDTO;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.finance.*;
import com.tsfyun.scm.entity.system.Subject;
import com.tsfyun.scm.entity.system.SubjectBank;
import com.tsfyun.scm.entity.system.SubjectOverseas;
import com.tsfyun.scm.entity.system.SubjectOverseasBank;
import com.tsfyun.scm.enums.SubjectTypeEnum;
import com.tsfyun.scm.mapper.finance.ExpPaymentAccountMapper;
import com.tsfyun.scm.service.common.ICommonService;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.finance.*;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.order.IExpOrderService;
import com.tsfyun.scm.service.support.ITaskNoticeContentService;
import com.tsfyun.scm.service.system.*;
import com.tsfyun.scm.vo.finance.*;
import com.tsfyun.scm.vo.report.ExpOneVoteTheEndVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 出口付款单 服务实现类
 * </p>
 *
 *
 * @since 2021-10-18
 */
@Service
public class ExpPaymentAccountServiceImpl extends ServiceImpl<ExpPaymentAccount> implements IExpPaymentAccountService {

    @Autowired
    private ExpPaymentAccountMapper expPaymentAccountMapper;
    @Autowired
    private IOverseasReceivingAccountService overseasReceivingAccountService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ISerialNumberService serialNumberService;
    @Autowired
    private IStatusHistoryService statusHistoryService;
    @Autowired
    private ITaskNoticeContentService taskNoticeContentService;
    @Autowired
    private ICommonService commonService;
    @Autowired
    private ISubjectOverseasService subjectOverseasService;
    @Autowired
    private ISubjectOverseasBankService subjectOverseasBankService;
    @Autowired
    private ISubjectService subjectService;
    @Autowired
    private ISubjectBankService subjectBankService;
    @Autowired
    private IOverseasReceivingOrderService overseasReceivingOrderService;
    @Autowired
    private ISettlementAccountService settlementAccountService;
    @Autowired
    private IExpPaymentAccountOrderService expPaymentAccountOrderService;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private IExpOrderService expOrderService;

    @Override
    public PageInfo<ExpPaymentAccountVO> pageList(ExpPaymentAccountQTO qto) {
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        Map<String,Object> params = beanMapper.map(qto, Map.class);
        List<ExpPaymentAccountVO> list = expPaymentAccountMapper.list(params);
        return new PageInfo<>(list);
    }

    @Override
    public ExpPaymentAccountPlusVO detail(Long id, String operation) {
        ExpPaymentAccount expPaymentAccount = super.getById(id);
        Optional.ofNullable(expPaymentAccount).orElseThrow(()->new ServiceException("付款单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.of(operation),expPaymentAccount.getStatusId());
        ExpPaymentAccountPlusVO expPaymentAccountPlusVO = new ExpPaymentAccountPlusVO();
        ExpPaymentAccountVO expPaymentAccountVO = beanMapper.map(expPaymentAccount,ExpPaymentAccountVO.class);
        expPaymentAccountPlusVO.setExpPaymentAccount(expPaymentAccountVO);
        Customer customer = customerService.getById(expPaymentAccount.getCustomerId());
        expPaymentAccountVO.setCustomerName(customer.getName());
        return expPaymentAccountPlusVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePaymentAccount(ExpPaymentAccountDTO dto) {
        // 初始化付款单信息
        CreatePaymentVO createPaymentVO = overseasReceivingAccountService.initPayment(dto.getOverseasAccountId());
        ExpPaymentAccount paymentAccount = new ExpPaymentAccount();
        paymentAccount.setCustomerId(createPaymentVO.getCustomerId());
        paymentAccount.setOverseasAccountId(createPaymentVO.getOverseasAccountId());
        paymentAccount.setAccountNo(createPaymentVO.getAccountNo());
        paymentAccount.setOrderNo(createPaymentVO.getOrderNo());
        paymentAccount.setClientNo(createPaymentVO.getClientNo());
        ExpPaymentAccountStatusEnum statusEnum = ExpPaymentAccountStatusEnum.WAIT_CONFIRM;
        paymentAccount.setStatusId(statusEnum.getCode());
        paymentAccount.setClaimPaymentDate(dto.getClaimPaymentDate());
        paymentAccount.setPayeeName(dto.getPayeeName());
        paymentAccount.setPayeeBank(dto.getPayeeBank());
        paymentAccount.setPayeeBankNo(dto.getPayeeBankNo());
        paymentAccount.setMemo(dto.getMemo());
        String docNo = serialNumberService.generateDocNo(SerialNumberTypeEnum.EXP_PAYMENT);
        paymentAccount.setDocNo(docNo);
        paymentAccount.setAccountValue(createPaymentVO.getAccountValue());
        try {
            super.saveNonNull(paymentAccount);
        } catch (DuplicateKeyException e) {
            if (e.getMessage().contains("UK_exp_payment_account_doc_no")) {
                throw new ServiceException("付款单号已经存在，请稍后重试");
            } else {
                throw new ServiceException("保存失败，请检查填写数据是否合法");
            }
        }
        // 修改收款单已付款
        OverseasReceivingAccount overseasReceivingAccount = overseasReceivingAccountService.getById(paymentAccount.getOverseasAccountId());
        overseasReceivingAccount.setIsPayment(Boolean.TRUE);
        overseasReceivingAccount.setPaymentId(paymentAccount.getId());
        overseasReceivingAccountService.updateById(overseasReceivingAccount);



        List<ExpPaymentAccountOrder> expPaymentAccountOrderList = new ArrayList<>();
        // 根据收款单查询结汇明细
        List<OverseasReceivingOrder> overseasReceivingOrderVOList = overseasReceivingOrderService.findOverseasReceivingOrderByAccountId(overseasReceivingAccount.getId());
        for(OverseasReceivingOrder oroVO : overseasReceivingOrderVOList){
            ExpPaymentAccountOrder epao = new ExpPaymentAccountOrder();
            epao.setId(snowflake.nextId());
            epao.setOrderId(oroVO.getExpOrderId());
            epao.setAccountValue(oroVO.getSettlementValue());
            epao.setCustomsRate(overseasReceivingAccount.getCustomsRate());
            epao.setPaymentAccountId(paymentAccount.getId());
            expPaymentAccountOrderList.add(epao);
        }
        // 保存付款订单明细
        expPaymentAccountOrderService.savaBatch(expPaymentAccountOrderList);

        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_CREATE,
                paymentAccount.getId().toString(), ExpPaymentAccount.class.getName(),
                statusEnum.getCode(),statusEnum.getName(),"收款登记");

        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.EXPPAYMENTACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(paymentAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_CONFIRM.getCode());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口付款单【%s】需要您确认。",createPaymentVO.getCustomerName(),paymentAccount.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",paymentAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removePaymentAccount(Long id) {
        ExpPaymentAccount paymentAccount = super.getById(id);
        Optional.ofNullable(paymentAccount).orElseThrow(()->new ServiceException("付款单不存在"));
        // 修改收款单已付款
        OverseasReceivingAccount overseasReceivingAccount = overseasReceivingAccountService.getById(paymentAccount.getOverseasAccountId());
        overseasReceivingAccount.setIsPayment(Boolean.FALSE);
        overseasReceivingAccount.setPaymentId(null);
        overseasReceivingAccountService.updateById(overseasReceivingAccount);

        expPaymentAccountOrderService.removeByPaymentId(id);
        //删除历史状态
        statusHistoryService.removeHistory(id.toString(), ExpPaymentAccount.class.getName());
        //清空任务通知
        clearTaskNotice(id);
        super.removeById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirm(TaskDTO dto) {
        ExpPaymentAccount paymentAccount = commonService.changeDocumentStatus(dto);
        // 清空任务通知
        clearTaskNotice(paymentAccount.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.EXPPAYMENTACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(paymentAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_BANK.getCode());
        Customer customer = customerService.getById(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口付款单【%s】请您尽快确定付款行。",customer.getName(),paymentAccount.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",paymentAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void backConfirm(TaskDTO dto) {
        ExpPaymentAccount paymentAccount = commonService.changeDocumentStatus(dto);
        // 清空任务通知
        clearTaskNotice(paymentAccount.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.EXPPAYMENTACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(paymentAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_CONFIRM.getCode());
        Customer customer = customerService.getById(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口付款单【%s】已被退回：%s",customer.getName(),paymentAccount.getDocNo(),dto.getMemo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",paymentAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void backConfirmBank(TaskDTO dto) {
        ExpPaymentAccount paymentAccount = commonService.changeDocumentStatus(dto);
        // 清空任务通知
        clearTaskNotice(paymentAccount.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.EXPPAYMENTACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(paymentAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_BANK.getCode());
        Customer customer = customerService.getById(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("客户【%s】有一单出口付款单【%s】需要您重新确定付款行。",customer.getName(),paymentAccount.getDocNo()));
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",paymentAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmBank(ExpConfirmBankDTO dto) {
        ExpPaymentAccount paymentAccount = super.getById(dto.getId());
        Optional.ofNullable(paymentAccount).orElseThrow(()->new ServiceException("付款单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_BANK,paymentAccount.getStatusId());
        SubjectTypeEnum subjectType = SubjectTypeEnum.of(dto.getSubjectType());
        paymentAccount.setSubjectId(dto.getSubjectId());
        switch (subjectType){
            case ABROAD:
                // 境外主体
                SubjectOverseas subjectOverseas = subjectOverseasService.getById(paymentAccount.getSubjectId());
                Optional.ofNullable(subjectOverseas).orElseThrow(()->new ServiceException("境外付款主体不存在"));
                paymentAccount.setSubjectName(subjectOverseas.getName());
                // 境外银行
                SubjectOverseasBank subjectOverseasBank = subjectOverseasBankService.getById(dto.getSubjectBank());
                Optional.ofNullable(subjectOverseasBank).orElseThrow(()->new ServiceException("境外付款银行不存在"));
                paymentAccount.setSubjectBank(subjectOverseasBank.getName());
                paymentAccount.setSubjectBankNo(subjectOverseasBank.getAccount());
                break;
            case MAINLAND:
                // 境内主体
                Subject subject = subjectService.getById(paymentAccount.getSubjectId());
                Optional.ofNullable(subject).orElseThrow(()->new ServiceException("境内付款主体不存在"));
                paymentAccount.setSubjectName(subject.getName());
                // 境内银行
                SubjectBank subjectBank = subjectBankService.getById(dto.getSubjectBank());
                Optional.ofNullable(subjectBank).orElseThrow(()->new ServiceException("境内付款银行不存在"));
                paymentAccount.setSubjectBank(subjectBank.getName());
                paymentAccount.setSubjectBankNo(subjectBank.getAccount());
                break;

            default:
                throw new ServiceException("付款主体类型错误");
        }
        paymentAccount.setSubjectType(subjectType.getCode());
        ExpPaymentAccountStatusEnum historyStatus = ExpPaymentAccountStatusEnum.of(paymentAccount.getStatusId());
        ExpPaymentAccountStatusEnum statusEnum = ExpPaymentAccountStatusEnum.WAIT_PAY;
        paymentAccount.setStatusId(statusEnum.getCode());
        super.updateById(paymentAccount);

        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_BANK,
                paymentAccount.getId().toString(), ExpPaymentAccount.class.getName(),
                historyStatus.getCode(),historyStatus.getName(),
                statusEnum.getCode(),statusEnum.getName(),"确定付款银行");
        // 清空任务通知
        clearTaskNotice(paymentAccount.getId());
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.EXPPAYMENTACCOUNT.getCode());
        taskNoticeContentDTO.setDocumentId(paymentAccount.getId().toString());
        taskNoticeContentDTO.setCustomerId(paymentAccount.getCustomerId());
        taskNoticeContentDTO.setContent(String.format("出口付款单【%s】已确定付款行，请尽快完成付款。", paymentAccount.getDocNo()));
        taskNoticeContentDTO.setOperationCode(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_COMPLETED.getCode());
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",paymentAccount.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paymentCompleted(ExpPaymentCompletedDTO dto) {
        ExpPaymentAccount paymentAccount = super.getById(dto.getId());
        Optional.ofNullable(paymentAccount).orElseThrow(()->new ServiceException("付款单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_COMPLETED,paymentAccount.getStatusId());
        paymentAccount.setActualPaymentDate(dto.getActualPaymentDate());
        ExpPaymentAccountStatusEnum historyStatus = ExpPaymentAccountStatusEnum.of(paymentAccount.getStatusId());
        ExpPaymentAccountStatusEnum statusEnum = ExpPaymentAccountStatusEnum.COMPLETED;
        paymentAccount.setStatusId(statusEnum.getCode());
        super.updateById(paymentAccount);

        //记录历史状态
        statusHistoryService.saveHistory(DomainOprationEnum.EXP_PAYMENT_ACCOUNT_COMPLETED,
                paymentAccount.getId().toString(), ExpPaymentAccount.class.getName(),
                historyStatus.getCode(),historyStatus.getName(),
                statusEnum.getCode(),statusEnum.getName(),"确定付款完成");

        // 清空任务通知
        clearTaskNotice(paymentAccount.getId());
    }

    @Override
    public PrintExpPaymentAccountVO getPrintExpPaymentAccountData(Long id) {
        ExpPaymentAccount paymentAccount = super.getById(id);
        Optional.ofNullable(paymentAccount).orElseThrow(()->new ServiceException("付款单不存在"));
        PrintExpPaymentAccountVO printExpPaymentAccountVO =  new PrintExpPaymentAccountVO();
        printExpPaymentAccountVO.setPayeeName(paymentAccount.getPayeeName());
        printExpPaymentAccountVO.setPayeeBank(paymentAccount.getPayeeBank());
        printExpPaymentAccountVO.setPayeeBankNo(paymentAccount.getPayeeBankNo());
        printExpPaymentAccountVO.setClaimPaymentDate(paymentAccount.getClaimPaymentDate());
        printExpPaymentAccountVO.setAccountValue(paymentAccount.getAccountValue());
        printExpPaymentAccountVO.setAccountValueCn(NumberConvertUtil.number2Chinese(paymentAccount.getAccountValue().toString()));
        //获取结汇金额
        OverseasReceivingAccount overseasReceivingAccount = overseasReceivingAccountService.findByDocNo(paymentAccount.getAccountNo());
        printExpPaymentAccountVO.setSettlementValue(overseasReceivingAccount.getAccountValue().subtract(overseasReceivingAccount.getFee()).setScale(2, BigDecimal.ROUND_HALF_UP));
        printExpPaymentAccountVO.setCurrencyCode(overseasReceivingAccount.getCurrencyId());
        //获取结汇汇率
        SettlementAccount settlementAccount = settlementAccountService.findByOverseasReceivingAccountId(overseasReceivingAccount.getId());
        printExpPaymentAccountVO.setSettlementRate(settlementAccount.getCustomsRate());
        //获取订单信息
        List<OverseasReceivingOrderVO> overseasReceivingOrderVOS = overseasReceivingOrderService.findByAccountId(overseasReceivingAccount.getId());
        StringBuffer remark = new StringBuffer("");
        overseasReceivingOrderVOS.stream().forEach(overseasReceivingOrderVO -> {
            remark.append(LocalDateTimeUtils.formatTime(overseasReceivingOrderVO.getOrderDate(),"yyyy-MM-dd")).append("/").append(overseasReceivingOrderVO.getClientNo()).append("/").append(overseasReceivingOrderVO.getAccountValue()).append("、");
        });
        if(remark.toString().endsWith("、")) {
            remark.deleteCharAt(remark.length() - 1);
        }
        printExpPaymentAccountVO.setRemark(remark.toString());
        printExpPaymentAccountVO.setApplyUser(statusHistoryService.obtainOperator(paymentAccount.getId().toString(),DomainOprationEnum.EXP_PAYMENT_ACCOUNT_CREATE));
        printExpPaymentAccountVO.setReviewUser(statusHistoryService.obtainOperator(paymentAccount.getId().toString(),DomainOprationEnum.EXP_PAYMENT_ACCOUNT_CONFIRM));
        return printExpPaymentAccountVO;
    }

    @Override
    public List<ExpOneVoteTheEndVO> oneVoteTheEndByOrderId(Long orderId) {
        return expPaymentAccountMapper.oneVoteTheEndByOrderId(orderId);
    }

    //清空任务通知
    public void clearTaskNotice(Long id){
        taskNoticeContentService.deleteTaskNotice(DomainTypeEnum.EXPPAYMENTACCOUNT.getCode(), id, "");
    }
}
